//
//  smcJaikuAPI+oauth.m
//  juhu
//
//  Created by Christian Martick on 12.11.07.
//  Copyright 2007 Christian Martick. All rights reserved.
//
//	jaiku api - do all the oauth stuff
//
//	Copyright (C) 2007 Christian Martick, http://juhu-mac.blogspot.com
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//
//	parts taken from Jon Crosbys wonderfull obj-c oauth lib http://code.google.com/p/oauth
//

#import "smcJaikuAPI.h"

#import <openssl/MD5.h>
#import <openssl/evp.h>
#import <openssl/err.h>
#import <openssl/hmac.h>
#import <openssl/bio.h>

#define kSmcJaikuOAuthBaseURLString @"http://term.ie/oauth/example"
#define kSmcOAuthVersion @"1.0"
#define kSmcSignatureMethodPlaintext @"PLAINTEXT"
#define kSmcSignatureMethodHMACSHA1 @"HMAC-SHA1"

#define kSmcConsumerKey @"key"
#define kSmcConsumerSecret @"secret"

#define kSmcEndpointRequestToken @"request_token.php"
#define kSmcEndpointUserAuthorization @"authorize.php"
#define kSmcEndpointAccessToken @"access_token.php"

@implementation smcJaikuAPI (oauth)

#pragma mark private helpers

- (NSString *) _encodedURLString: (NSString *) str 
{
	NSString *result = (NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
		(CFStringRef) str, NULL, CFSTR("=?&+"), kCFStringEncodingUTF8);                   
	return result;
}

- (NSString *) _encodedURLParameterString: (NSString *) str 
{
    NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
		(CFStringRef) str, NULL, CFSTR(":/"), kCFStringEncodingUTF8);                                                                     
	return result;
}

- (NSString *) _encodedURLStringTotally: (NSString *) str
{
	NSString *result = (NSString *) CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
		(CFStringRef) str, NULL, CFSTR(":/=?&+"), kCFStringEncodingUTF8);                   
	return result;
	return result;
}

- (NSString *) _timestampNow
{
	// creates a string containing the seconds since 1970 00:00:00 gmt
	// this is the default timestamp for oauth (but services might specify their own)
	
	return [[NSNumber numberWithUnsignedInt: (int) [[NSDate date] timeIntervalSince1970]] stringValue];
}

- (NSString *) _nonceWithTimestamp: (NSString *) stamp
{
	// creates a random string - a nonce - which is unique for the given time stamp
	// simply doing the md5 checksum for the stamp string (using openssl library)
	
	// the string data
	NSData *tmp = [stamp dataUsingEncoding: NSUTF8StringEncoding];
	
	// and go for it
	unsigned char *result = MD5([tmp bytes], [tmp length], NULL);
	if (result) 
	{
		NSMutableString *strResult = [NSMutableString string];
		for (int i = 0; i < MD5_DIGEST_LENGTH; i++) 
			[strResult appendFormat: @"%02x", (int)(result[i])];
	
		return [[strResult copy] autorelease];
	} else 
		return @"";
}

- (NSString *) _signatureWithClearText: (NSString *) clear secret: (NSString *) sec andType: (NSString *) type
{
	// provides signatures for the given text, secret and type
	// right now only PLAINTEXT and HMAC-SHA1 are supported, HMAC-SHA1 will be default fall back
		
	if ([type isEqualToString: @"PLAINTEXT"])
	{
		// not much todo here
		return sec;
	} else
	{
		// HMAC-SHA1 default fall back

		NSData *secData = [sec dataUsingEncoding: NSUTF8StringEncoding];
		NSData *clearData = [clear dataUsingEncoding: NSUTF8StringEncoding];

		HMAC_CTX context;
		unsigned char result[EVP_MAX_MD_SIZE];
		unsigned int resultLength;
		
		HMAC_CTX_init(&context);
		HMAC_Init(&context, [secData bytes], [secData length], EVP_sha1());
		HMAC_Update(&context, [clearData bytes], [clearData length]);
		HMAC_Final(&context, result, &resultLength);
		
		 // base64...
		char *base64;
		
		BIO *b = BIO_new(BIO_s_mem());
		BIO *filter = BIO_new(BIO_f_base64());
		b = BIO_push(filter, b);
		BIO_write(b, result, resultLength);
		BIO_flush(b);
		
		long base64Length = BIO_get_mem_data(b, &base64);
		base64[base64Length - 1] = '\0';
		
		NSString *ret = [NSString stringWithCString: base64 encoding: NSUTF8StringEncoding];
		
		BIO_free_all(b);
		HMAC_CTX_cleanup(&context);
			
		return ret;
	}
}

#pragma mark basic public oauth helpers

- (NSURL *) signedURLWithBaseURLString: (NSString *) aUrl parameters: (NSDictionary *) parameters signatureMethod: (NSString *) signatureMethod 
httpMethod: (NSString *) httpMethod onlyWithConsumerSecret: (BOOL) yn
{
	// create parameter string from dictionary
	NSMutableString *tmp = [NSMutableString string];
	NSMutableString *tmptwo = [NSMutableString string];
	NSEnumerator *enumerator = [parameters keyEnumerator];
	id key;
 
	while ((key = [enumerator nextObject]))
	{
		[tmp appendString: [NSString stringWithFormat: @"&%@=%@", key, [parameters objectForKey: key]]];
		[tmptwo appendString: [NSString stringWithFormat: @"&%@=%@", [self _encodedURLStringTotally: key], 
			[self _encodedURLStringTotally: [parameters objectForKey: key]]]];
	}
	
	NSString *paramString = [tmp substringFromIndex: 1];
	NSString *anotherOne = [tmptwo substringFromIndex: 1];
	
	NSLog(@"paramstring:%@\n\n", paramString);
	
	// create signature base string
	NSString *baseString = [NSString stringWithFormat: @"%@&%@&%@", httpMethod, 
		[self _encodedURLStringTotally: aUrl], [self _encodedURLStringTotally: paramString]];
NSLog(@"\n\nBase: %@\n\n", baseString);	
	// user both consumer secret and access secret?
	NSMutableString *secret = [NSMutableString stringWithString: kSmcConsumerSecret];
	if (!yn)
		[secret appendString: [NSString stringWithFormat: @"&%@", _accessSecret]];
	NSLog(@"here");

	// create complete parameter string
	NSString *completeParameterString = [NSString stringWithFormat: @"?%@&oauth_signature=%@", 
		anotherOne, 
		[self _encodedURLStringTotally: [self _signatureWithClearText: baseString secret: secret andType: signatureMethod]]];
		
	// create url string with signature
	NSString *completeURLString = [NSString stringWithFormat: @"%@%@", aUrl, completeParameterString];
			
	NSLog(@"here");
	return [NSURL URLWithString: completeURLString];
}

#pragma mark oauth tasks

- (id) requestToken
{
	// perform the request_token request
	
	// create timestamp and nonce
	NSString *timestamp = [self _timestampNow];
	NSString *nonce = [self _nonceWithTimestamp: timestamp];
	
	// create url string with signature
	
	NSURL *completeURL = [self signedURLWithBaseURLString: [NSString stringWithFormat: @"%@/%@", kSmcJaikuOAuthBaseURLString, kSmcEndpointRequestToken] 
		parameters: [NSDictionary dictionaryWithObjectsAndKeys: 
			kSmcOAuthVersion, @"oauth_version",
			nonce, @"oauth_nonce",
			timestamp, @"oauth_timestamp",
			kSmcConsumerKey, @"oauth_consumer_key",
			kSmcSignatureMethodHMACSHA1, @"oauth_signature_method", nil]
		signatureMethod: kSmcSignatureMethodHMACSHA1  
		httpMethod: @"GET" 
		onlyWithConsumerSecret: YES];

	
	NSLog(@"\n\nstrsignless string: %@",@"JJ");
	NSLog(@"\n\ncompplete: %@", completeURL);
	
	NSError *error = nil;
	NSURLResponse *response;
	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: completeURL
			cachePolicy: NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
	[request setHTTPMethod: @"GET"];
	[request addValue: JUHUCLIENT_VERSION forHTTPHeaderField: @"User-Agent"];
	
	NSData *data = [NSURLConnection sendSynchronousRequest: request
			returningResponse: &response
			error: &error];

	NSMutableString *htmlString = [self makeCleanUTF8MutableStringFromData: data];
	
	NSLog(@"\n\nresult: %@", htmlString);

	// figure out if something went wrong
	if (((NSHTTPURLResponse *) [response statusCode]) != 200)
	{
		// no 'okay', what happend?
		NSLog(@"request token failed.\n");
		int statusCode = (NSHTTPURLResponse *) [response statusCode];
		switch (statusCode)
		{
			case 400:
				NSLog(@"Bad request");
				break;
			case 401:
				NSLog(@"Unauthorized: Invalid Signature, Invalid Consumer Key, Invalid / expired Token, Invalid / used nonce");
				break;
		}
	} else
	{
		// get oauth_token and oauth token_secret
		
	}
}

- (void) requestUserAuthorization
{
	// request user authorization via browser
	
	// create url
	NSString *urlString = [NSString stringWithFormat: @"%@/%@?oauth_token=%@", kSmcJaikuOAuthBaseURLString, kSmcEndpointUserAuthorization, _currentOAuthToken];
	
	// and open it in the browser
	[[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: urlString]];
}

- (id) accessToken
{
	// perform access token request
	
}

@end